/*
** Haaf's Game Engine 1.7
** Copyright (C) 2003-2007, Relish Games
** hge.relishgames.com
**
** hgeColor*** helper classes
*/


#ifndef HGECOLOR_H
#define HGECOLOR_H


#include "hge.h"


#define hgeColor hgeColorRGB

inline void ColorClamp(float &x) { if(x<0.0f) x=0.0f; if(x>1.0f) x=1.0f; }


class hgeColorRGB
{
public:
	float		r,g,b,a;

	hgeColorRGB(float _r, float _g, float _b, float _a) { r=_r; g=_g; b=_b; a=_a; }
	hgeColorRGB(DWORD col) { SetHWColor(col); }
	hgeColorRGB() { r=g=b=a=0; }

	hgeColorRGB		operator-  (const hgeColorRGB &c) const { return hgeColorRGB(r-c.r, g-c.g, b-c.b, a-c.a); }
	hgeColorRGB		operator+  (const hgeColorRGB &c) const { return hgeColorRGB(r+c.r, g+c.g, b+c.b, a+c.a); }
	hgeColorRGB		operator*  (const hgeColorRGB &c) const { return hgeColorRGB(r*c.r, g*c.g, b*c.b, a*c.a); }
	hgeColorRGB&	operator-= (const hgeColorRGB &c)		{ r-=c.r; g-=c.g; b-=c.b; a-=c.a; return *this;   }
	hgeColorRGB&	operator+= (const hgeColorRGB &c)		{ r+=c.r; g+=c.g; b+=c.b; a+=c.a; return *this;   }
	bool			operator== (const hgeColorRGB &c) const { return (r==c.r && g==c.g && b==c.b && a==c.a);  }
	bool			operator!= (const hgeColorRGB &c) const { return (r!=c.r || g!=c.g || b!=c.b || a!=c.a);  }

	hgeColorRGB		operator/  (const float scalar) const { return hgeColorRGB(r/scalar, g/scalar, b/scalar, a/scalar); }
	hgeColorRGB		operator*  (const float scalar) const { return hgeColorRGB(r*scalar, g*scalar, b*scalar, a*scalar); }
	hgeColorRGB&	operator*= (const float scalar)		  { r*=scalar; g*=scalar; b*=scalar; a*=scalar; return *this;   }

	void			Clamp() { ColorClamp(r); ColorClamp(g); ColorClamp(b); ColorClamp(a); }
	void			SetHWColor(DWORD col) {	a = (col>>24)/255.0f; r = ((col>>16) & 0xFF)/255.0f; g = ((col>>8) & 0xFF)/255.0f; b = (col & 0xFF)/255.0f;	}
	DWORD			GetHWColor() const { return (DWORD(a*255.0f)<<24) + (DWORD(r*255.0f)<<16) + (DWORD(g*255.0f)<<8) + DWORD(b*255.0f);	}
};

inline hgeColorRGB operator* (const float sc, const hgeColorRGB &c) { return c*sc; }


class hgeColorHSV
{
public:
	float		h,s,v,a;

	hgeColorHSV(float _h, float _s, float _v, float _a) { h=_h; s=_s; v=_v; a=_a; }
	hgeColorHSV(DWORD col) { SetHWColor(col); }
	hgeColorHSV() { h=s=v=a=0; }

	hgeColorHSV		operator-  (const hgeColorHSV &c) const { return hgeColorHSV(h-c.h, s-c.s, v-c.v, a-c.a); }
	hgeColorHSV		operator+  (const hgeColorHSV &c) const { return hgeColorHSV(h+c.h, s+c.s, v+c.v, a+c.a); }
	hgeColorHSV		operator*  (const hgeColorHSV &c) const { return hgeColorHSV(h*c.h, s*c.s, v*c.v, a*c.a); }
	hgeColorHSV&	operator-= (const hgeColorHSV &c)		{ h-=c.h; s-=c.s; v-=c.v; a-=c.a; return *this;   }
	hgeColorHSV&	operator+= (const hgeColorHSV &c)		{ h+=c.h; s+=c.s; v+=c.v; a+=c.a; return *this;   }
	bool			operator== (const hgeColorHSV &c) const { return (h==c.h && s==c.s && v==c.v && a==c.a);  }
	bool			operator!= (const hgeColorHSV &c) const { return (h!=c.h || s!=c.s || v!=c.v || a!=c.a);  }

	hgeColorHSV		operator/  (const float scalar) const { return hgeColorHSV(h/scalar, s/scalar, v/scalar, a/scalar); }
	hgeColorHSV		operator*  (const float scalar) const { return hgeColorHSV(h*scalar, s*scalar, v*scalar, a*scalar); }
	hgeColorHSV&	operator*= (const float scalar)		  { h*=scalar; s*=scalar; v*=scalar; a*=scalar; return *this;   }

	void			Clamp() { ColorClamp(h); ColorClamp(s); ColorClamp(v); ColorClamp(a); }
	void			SetHWColor(DWORD col);
	DWORD			GetHWColor() const;
};

inline hgeColorHSV operator* (const float sc, const hgeColorHSV &c) { return c*sc; }


typedef UINT32 argb_t;    //!< 32 bit ARGB representation of a colour.

/*!
\brief
Class representing colour values within the system.
*/
class colour
{
public:
	/*************************************************************************
	Construction & Destruction
	*************************************************************************/
	colour(void);
	colour(const colour& val);
	colour(float red, float green, float blue, float alpha = 1.0f);
	colour(argb_t argb);

	/*************************************************************************
	Accessors
	*************************************************************************/
	argb_t	getARGB(void) const
	{
		if (!d_argbValid)
		{
			d_argb = calculateARGB();
			d_argbValid = true;
		}

		return d_argb;
	}

	float	getAlpha(void) const	{return d_alpha;}
	float	getRed(void) const		{return d_red;}
	float	getGreen(void) const	{return d_green;}
	float	getBlue(void) const		{return d_blue;}

	float	getHue(void) const;
	float	getSaturation(void) const;
	float	getLumination(void) const;


	/*************************************************************************
	Manipulators
	*************************************************************************/
	void	setARGB(argb_t argb);
	inline void setAlpha(float alpha)
	{
		d_argbValid = false;
		d_alpha = alpha;
	}

	inline void setRed(float red)
	{   
		d_argbValid = false;
		d_red = red;
	}

	inline void setGreen(float green)
	{
		d_argbValid = false;
		d_green = green;
	}

	inline void setBlue(float blue)
	{
		d_argbValid = false;
		d_blue = blue;
	}

	inline void set(float red, float green, float blue, float alpha = 1.0f)
	{
		d_argbValid = false;
		d_alpha = alpha;
		d_red = red;
		d_green = green;
		d_blue = blue;
	}

	inline void setRGB(float red, float green, float blue)
	{
		d_argbValid = false;
		d_red = red;
		d_green = green;
		d_blue = blue;
	}

	inline void setRGB(const colour& val)
	{
		d_red = val.d_red;
		d_green = val.d_green;
		d_blue = val.d_blue;
		if (d_argbValid)
		{
			d_argbValid = val.d_argbValid;
			if (d_argbValid)
				d_argb = (d_argb & 0xFF000000) | (val.d_argb & 0x00FFFFFF);
		}
	}

	void	setHSL(float hue, float saturation, float luminance, float alpha = 1.0f);

	void	invertColour(void);
	void	invertColourWithAlpha(void);

	/*************************************************************************
	Operators
	*************************************************************************/
	inline colour& operator=(argb_t val)
	{
		setARGB(val);
		return *this;
	}

	inline colour& operator=(const colour& val)
	{
		d_alpha = val.d_alpha;
		d_red   = val.d_red;
		d_green = val.d_green;
		d_blue  = val.d_blue;
		d_argb  = val.d_argb;
		d_argbValid = val.d_argbValid;

		return *this;
	}

	inline colour& operator&=(argb_t val)
	{
		setARGB(getARGB() & val);
		return *this;
	}

	inline colour& operator&=(const colour& val)
	{
		setARGB(getARGB() & val.getARGB());
		return *this;
	}

	inline colour& operator|=(argb_t val)
	{
		setARGB(getARGB() | val);
		return *this;
	}

	inline colour& operator|=(const colour& val)
	{
		setARGB(getARGB() | val.getARGB());
		return *this;
	}

	inline colour& operator<<=(int val)
	{
		setARGB(getARGB() << val);
		return *this;
	}

	inline colour& operator>>=(int val)
	{
		setARGB(getARGB() >> val);
		return *this;
	}

	inline colour operator+(const colour& val) const
	{
		return colour(
			d_red   + val.d_red, 
			d_green + val.d_green, 
			d_blue  + val.d_blue,
			d_alpha + val.d_alpha
			);
	}

	inline colour operator-(const colour& val) const
	{
		return colour(
			d_red   - val.d_red,
			d_green - val.d_green,
			d_blue  - val.d_blue,
			d_alpha - val.d_alpha
			);
	}

	inline colour operator*(const float val) const
	{       
		return colour(
			d_red   * val, 
			d_green * val, 
			d_blue  * val,
			d_alpha * val 
			);  
	}

	inline colour& operator*=(const colour& val)
	{
		d_red *= val.d_red;
		d_blue *= val.d_blue;
		d_green *= val.d_green;
		d_alpha *= val.d_alpha;

		d_argbValid = false;

		return *this;
	}

	/*************************************************************************
	Compare operators
	*************************************************************************/
	inline bool operator==(const colour& rhs) const
	{
		return d_red   == rhs.d_red   &&
			d_green == rhs.d_green &&
			d_blue  == rhs.d_blue  &&
			d_alpha == rhs.d_alpha;
	}

	inline bool operator!=(const colour& rhs) const
	{
		return !(*this == rhs);
	}

	//
	// Conversion operators
	//
	operator argb_t() const		{return getARGB();}

private:
	/*************************************************************************
	Implementation Methods
	*************************************************************************/
	/*!
	\brief
	calculate and return the ARGB value based on the current colour component values.
	*/
	argb_t	calculateARGB(void) const;

	/*************************************************************************
	Implementation Data
	*************************************************************************/
	float d_alpha, d_red, d_green, d_blue;		//!< Colour components.
	mutable argb_t d_argb;						//!< Colour as ARGB value.
	mutable bool d_argbValid;					//!< True if argb value is valid.
};


/*!
\brief
Class that holds details of colours for the four corners of a rectangle.
*/
class ColourRect
{
public:
	ColourRect(void);
	ColourRect(const colour& col);
	ColourRect(const colour& top_left, const colour& top_right, const colour& bottom_left, const colour& bottom_right);

	//Set the alpha value to use for all four corners of the ColourRect.
	void	setAlpha(float alpha);

	//Set the alpha value to use for the top edge of the ColourRect.
	void	setTopAlpha(float alpha);

	//Set the alpha value to use for the bottom edge of the ColourRect.
	void	setBottomAlpha(float alpha);

	//Set the alpha value to use for the left edge of the ColourRect.
	void	setLeftAlpha(float alpha);

	//Set the alpha value to use for the right edge of the ColourRect.
	void	setRightAlpha(float alpha);

	//Determinate the ColourRect is monochromatic or variegated.
	bool	isMonochromatic() const;

	//Gets a portion of this ColourRect as a subset ColourRect
	ColourRect getSubRectangle( float left, float right, float top, float bottom ) const;

	//Get the colour at a point in the rectangle
	colour getColourAtPoint( float x, float y ) const;

	//Set the colour of all four corners simultaneously.
	void	setColours(const colour& col);

	//Module the alpha components of each corner's colour by a constant.
	void	modulateAlpha(float alpha);

	//Modulate all components of this colour rect with corresponding components from another colour rect.
	ColourRect& operator*=(const ColourRect& other);

	colour	d_top_left, d_top_right, d_bottom_left, d_bottom_right;		//<! ColourRect component colours
};

#endif
